package org.example.service.impl;

import cn.hutool.core.collection.ConcurrentHashSet;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.ann.DynamicExecutor;
import org.example.ann.DynamicJob;
import org.example.config.ExecutorConfig;
import org.example.config.XxlJobConfig;
import org.example.config.XxlJobScan;
import org.example.entity.dto.XxlJobGroupResponse;
import org.example.entity.dto.XxlJobInfoReq;
import org.example.service.XxlJobConfigService;
import org.example.service.XxlJobExecutorApi;
import org.example.util.Opt;
import org.example.util.Opts;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

/**
 * @author zengxiaoyu
 * @date 2025/9/11 17:05
 */
@Component
@Slf4j

public class XxlJobConfigServiceImpl implements XxlJobConfigService, ApplicationContextAware {

    @Autowired
    private  XxlJobConfig xxlJobConfig;

    private ConfigurableApplicationContext applicationContext;


    @Autowired
    private  XxlJobExecutorApi xxlJobExecutorApi;

    public static final Set<String> EXECUTOR_NAMES = new ConcurrentHashSet<>();



    @Override
    public void createIfNotExist(ExecutorConfig executor) throws Exception {
        XxlJobGroupResponse allExecutors = xxlJobExecutorApi.getAllExecutors(null);
        createIfNotExistInternal(allExecutors, executor);
    }

    private void createIfNotExistInternal(XxlJobGroupResponse allExecutors, ExecutorConfig executor) throws Exception {
        String appNameStr = executor.getAppName();
        Optional<XxlJobGroupResponse.GroupData> executorOpt = Opt.of(allExecutors).ofs(XxlJobGroupResponse::getData).filter(t -> t.getAppname().equals(appNameStr)).first();
        if(!executorOpt.isPresent()){
            log.info("xxlJobExecutor:{} not exist,create it",appNameStr);
            CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
                xxlJobExecutorApi.createExecutor(executor);
            });
            CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
                xxlJobExecutorApi.createExecutor(executor);
            });
            CompletableFuture.allOf(future1,future2).join();
        }
        registerExecutor(executor);
        log.info("xxlJobExecutor:{} create success",appNameStr);
        Integer jobGroupId = xxlJobExecutorApi.getJobGroupId(executor.getAppName());
        executor.setJobGroupId(jobGroupId);
    }



    @Override
    public void createIfNotExist(List<DynamicExecutor> executors) {
       Opts.of(executors).action(ts->{
           XxlJobGroupResponse allExecutors = xxlJobExecutorApi.getAllExecutors(null);
           ts.forEach(executor -> {
               try {
                   ExecutorConfig executorConfig = new ExecutorConfig(xxlJobConfig);
                   executorConfig.setAppName(executor.name());
                   executorConfig.setTitle(executor.title());
                   executorConfig.setPort(executor.port());
                   createIfNotExistInternal(allExecutors,executorConfig);
               } catch (Exception e) {
                   log.info("xxlJobExecutor:{} create error",executor.name());
                   throw new RuntimeException(e);
               }
           });
       });
    }

    @Override
    public void createJobIfNotExist(List<XxlJobScan.XxlJobInfoAnno> jobInfos) {
       Opts.of(jobInfos).action(ts->{
           for (XxlJobScan.XxlJobInfoAnno t : ts) {
               DynamicJob dynamicJob = t.getDynamicJob();
               XxlJobInfoReq xxlJobInfoReq = new XxlJobInfoReq();
               xxlJobInfoReq.setJobGroup(xxlJobExecutorApi.getJobGroupId(dynamicJob.executor().name()));
               xxlJobInfoReq.setJobDesc(dynamicJob.jobDesc());
               xxlJobInfoReq.setAuthor(xxlJobConfig.getAuthor());
               xxlJobInfoReq.setExecutorHandler(t.getXxlJob().value());
               xxlJobInfoReq.setExecutorParam(dynamicJob.executorParam());
               xxlJobInfoReq.setScheduleConf(dynamicJob.cron());
               xxlJobExecutorApi.createJobIfNotExist(xxlJobInfoReq);
           }
           log.info("xxlJob:{} create success",ts.size());
           xxlJobExecutorApi.removeUnusedJob(xxlJobConfig.getAuthor());
       });
    }

    private void registerExecutor(ExecutorConfig executor) throws Exception {
        // 向springboot中注 bean对象
        if(EXECUTOR_NAMES.contains(executor.getAppName())){
            log.info("xxlJobExecutor:{} has been registered",executor.getAppName());
        }else {
            XxlJobSpringExecutor xxlJobSpringExecutor = executor.buildExecutor();
            applicationContext.getBeanFactory().registerSingleton(executor.getAppName(), xxlJobSpringExecutor);
            // 手动启动执行器（因为注册时机可能已经过了Spring的自动启动时机）
            xxlJobSpringExecutor.start();
            EXECUTOR_NAMES.add(executor.getAppName());
            log.info("xxlJobExecutor:{} register success",executor.getAppName());
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext= (ConfigurableApplicationContext) applicationContext;
    }
}
